﻿package com.totallypracticalgames.unityflashbridge
{
	import flash.display.Loader;
	import flash.display.SimpleButton;
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.display.BitmapData;
	import flash.display.Bitmap;
	import flash.display.DisplayObject;
	import flash.external.ExternalInterface;
	import flash.net.URLRequest;
	import flash.net.LocalConnection;
	import flash.text.TextField;
	import flash.text.TextFormat;
	import flash.text.TextFormatAlign;
	import flash.events.Event;
	import flash.utils.ByteArray;  // for base64 stuff
    import com.dynamicflash.util.Base64;// There is also a base 64 decoder in mx.utils, but this only seems to be part of the Flex SDK
	
	//import nl.demonsters.debugger.MonsterDebugger;// for debugging. See http://demonsterdebugger.com/

	
	/**
	 * ...
	 * @author Roger Clark 
	 * @usage  Class to communicate with Unity3D via Javascript in the browser and launch SWF's 
	 */
	public class UnityFlashLauncher extends Sprite 
	{
		private var _backgroundImageHolder:Sprite;
		private var strUUData:String;
		private var loadedSWF:DisplayObject;
		private var loadedBackgroundImage:DisplayObject;
		private var loaderSWF:Loader = new Loader();

	//	private var mdb://MonsterDebugger = new //MonsterDebugger(this);
		

		private var _ufi:UnityFlashIntegrator = UnityFlashIntegrator.getInstance();		
		
		public function UnityFlashLauncher():void 
		{
			
			if (stage)
			{
				init();
			}
			else
			{
				addEventListener(Event.ADDED_TO_STAGE, init);
			}
		}
		
		public function init(e:Event = null):void 
		{
		
			//MonsterDebugger.trace(this,"init()");
			removeEventListener(Event.ADDED_TO_STAGE, init);
			// entry point
			
			/**
			 * Stage settings
			 */
			stage.align = StageAlign.TOP_LEFT;
			stage.scaleMode = StageScaleMode.NO_SCALE;
			


			strUUData="";
			loadedSWF=null;

			/**
			 * Setup callback functions for functions called from the browser JavaScript
			 */
			if (true)
			{
			ExternalInterface.addCallback("messageFromUnity2", messageFromUnity2 );// 2 arguement message
			ExternalInterface.addCallback("messageFromUnity3", messageFromUnity3 );// 3 arguement message
			ExternalInterface.addCallback("messageFromUnity4", messageFromUnity4 );// 4 arguement message
			ExternalInterface.addCallback("messageFromUnity5", messageFromUnity5 );// 5 arguement message
			}
			this.addEventListener(UnityFlashIntegrator.CHILD_EXIT, childExit);
			/**
			 * Background image holder
			 */
			_backgroundImageHolder = new Sprite();
			_backgroundImageHolder.y = 0;
			_backgroundImageHolder.x = 0;
			this.addChild(_backgroundImageHolder); 
			
			this.addChild(loaderSWF);	// Add Loader to display list in fromt of the background image holder
		}
		
		private function sendVarsToUnity():void
		{
			for(var i:* in _ufi.unityVars)
			{
				ExternalInterface.call("gameUnity.flashToUnity","GameController","flashSetVariable",String(i)+"="+String(_ufi.unityVars[i]));
			}
			_ufi.unityVars = null;// remove existing vars
		}
		
		private function childExit(evt:Event):void
		{
			loaderSWF.unload();
			sendVarsToUnity();
			//_ufi.unityVars=null; // Delete the vars object as we no longer need it
			ExternalInterface.call("gameUnity.showUnity");
			ExternalInterface.call("gameUnity.flashToUnity","GameController", "stateMessageFromFlash",5);// Tell unity that it can unpause its self.
			
		}
		
		// Messages (events) from the loaded SWF
		public function messageLoadedSWF(msg:Array):void 
		{
			switch (msg[0])
			{
				case "finished":					
					if (msg[1]=="1")
					{	
						sendVarsToUnity();
						//_ufi.unityVars=null; // Delete the vars object as we no longer need it
						ExternalInterface.call("gameUnity.showUnity");
						ExternalInterface.call("gameUnity.flashToUnity","GameController", "stateMessageFromFlash",5);// Tell unity that it can unpause its self.
					}
					break;
				default:
					break;
			}
        }
		
		// 2 Arg message from unity		
		private function messageFromUnity2(id:int,msg:String):void
		{
			//MonsterDebugger.trace(this,"messageFromUnity2 "+msg+"\n" );
			//title.appendText("messageFromUnity2 "+msg+"\n" );
			switch(msg)
			{
				case "dataStart":
					// Unity is about to send new background image data
					strUUData="";// Clear the string buffer
					
					if (loadedBackgroundImage!=null)
					{
						_backgroundImageHolder.removeChild(loadedBackgroundImage);// unload any existing background image
					}
					break;
				case "dataEnd":
					// All background image data has been sent. So we need to convert it back into a PNG and display it
					var loader:Loader = new Loader();
					loader.contentLoaderInfo.addEventListener(Event.COMPLETE, getBitmapData);// setup callback to process the image data

					loader.loadBytes(Base64.decodeToByteArray(strUUData));// give the loader the decompressed image data.
					break;		
			}
			ExternalInterface.call("gameUnity.flashToUnity","GameController","ackMessageFromFlash",id);// Tell that we have received and actioned this message id
		}
		
	
		// Message handling function for messages from Unity
		// Data is name / value pair
		private function messageFromUnity3(id:int,msg:String,d:String):void
		{
			//MonsterDebugger.trace(this,"messageFromUnity3 "+msg+" "+d+"\n" );		
			switch(msg)
			{
				case "loadswf":
					loaderSWF.unload();
					
					loaderSWF.contentLoaderInfo.addEventListener(Event.COMPLETE, onSWFLoaded);// setup callback to process the swf when its finished loading
					loaderSWF.load(new URLRequest(d));
					break;			
				case "data":
					strUUData += d;// Concatenate the data onto the end of the rest of the data
					break;
				default:
					break;
			}
			ExternalInterface.call("gameUnity.flashToUnity","GameController","ackMessageFromFlash",id);// Tell that we have received and actioned this message id
		}
		
		// 3 Arg message from Unity		
		private function messageFromUnity4(id:int,msg:String,arg1:String,arg2:String):void
		{
			//MonsterDebugger.trace(this,"messageFromUnity4 "+msg+" "+arg1+" "+arg2+"\n" );// debug messages
			ExternalInterface.call("gameUnity.flashToUnity","GameController","ackMessageFromFlash",id);// Tell that we have recieved and actioned this message id
		}
		// 4 Arg message from unity	. Used to send variables	
		private function messageFromUnity5(id:int,msg:String,arg1:String,arg2:String,arg3:String):void
		{
			//MonsterDebugger.trace(this,"messageFromUnity5 "+msg+" "+arg1+" "+arg2+" "+arg3+"\n" );// debug messages
			switch(msg)
			{
				case "setFlashVar":
					if (_ufi.unityVars==null)
					{
						_ufi.unityVars=new Object();
					}
					switch(arg3)
					{
						case "System.Int32":
							_ufi.unityVars[arg1]= int(arg2);
							break;
						case "System.String":
							_ufi.unityVars[arg1]= String(arg2);
							break;
						case "System.Single":
						case "System.Double":
							_ufi.unityVars[arg1]= Number(arg2);
						break;					
						case "System.Boolean":	
							if (arg2.toLowerCase()=="true")
							{
								_ufi.unityVars[arg1]= Boolean(true);
							}
							else
							{
								_ufi.unityVars[arg1]= Boolean(false);
							}
							break;		
						default:
						// If we don't recognise the type, just use a string!
							_ufi.unityVars[arg1]= String(arg2);
							break;								
					}
					break;
			}
			ExternalInterface.call("gameUnity.flashToUnity","GameController","ackMessageFromFlash",id);// Tell that we have received and actioned this message id
		}
		
		// Called - callback - when child Swf is loaded
		private function onSWFLoaded(e:Event):void
		{
			ExternalInterface.call("gameUnity.hideUnity");// Tell Unity that the swf has been loaded
		}
		
		// Function to process the image data and display it. 
		private function getBitmapData(e:Event):void  
		{
			//MonsterDebugger.trace(this,"getBitmapData " );// debug messages

			loadedBackgroundImage=_backgroundImageHolder.addChild(Bitmap(e.target.content));
		}

	} // class
} // package